package ast.exp;

import icg.EQLabel;
import icg.EQTemp;
import icg.Quadruple;
import ts.SymbolTable;
import ast.Node;
import ast.type.OpType;
import ast.type.Type;

public class BinExp extends Expression {
	private Expression exp1;
	private Expression exp2;
	private OpType op;

	public BinExp(OpType op, Expression exp1, Expression exp2) {
		this.exp1 = exp1;
		this.exp2 = exp2;
		this.op = op;
	}

	@Override
	public String toString() {
		return "<BinExp op=\"" + op + "\">\n" + exp1 + "\n" + exp2 + "\n"
				+ "</BinExp>";
	}

	@Override
	public Type typeCheck() {
		Type t1 = exp1.typeCheck();
		Type t2 = exp2.typeCheck();
		type = Type.ERROR;
		if (t1 != Type.ERROR && t2 != Type.ERROR) {
			if (op == OpType.PLUS || op == OpType.MINUS || op == OpType.DIV
					|| op == OpType.MULT || op == OpType.MOD)
				if (t1 == Type.INTEGER && t2 == Type.INTEGER)
					return Type.INTEGER;
			if (op == OpType.DIFERENTE || op == OpType.IGUAL
					|| op == OpType.MAIOR || op == OpType.MAIORIGUAL
					|| op == OpType.MENOR || op == OpType.MENORIGUAL)
				if (t1 == Type.INTEGER && t2 == Type.INTEGER)
					return Type.BOOLEAN;
			if (op == OpType.AND || op == OpType.OR)
				if (t1 == Type.BOOLEAN && t2 == Type.BOOLEAN)
					return Type.BOOLEAN;
		}
		SymbolTable.errors++;
		System.out.println("invalid binary expression");
		return type;
	}

	@Override
	public void genCode() {
		
		if (op == OpType.DIFERENTE || op == OpType.IGUAL || op == OpType.MAIOR
				|| op == OpType.MAIORIGUAL || op == OpType.MENOR
				|| op == OpType.MENORIGUAL) {
			exp1.genCode();
			exp2.genCode();
			this.addr = new EQTemp();
			if (this.falseL.equals(EQLabel.FALL)) {
				Node.icg.gen(Quadruple.branch(op, this.trueL, exp1.addr, exp2.addr));
			} else if (this.trueL.equals(EQLabel.FALL)) {
				OpType notOp = null;
				switch (op) {
				case DIFERENTE:
					notOp = OpType.IGUAL;
					break;
				case IGUAL:
					notOp = OpType.DIFERENTE;
					break;
				case MENOR:
					notOp = OpType.MAIORIGUAL;
					break;
				case MENORIGUAL:
					notOp = OpType.MAIOR;
					break;
				case MAIOR:
					notOp = OpType.MENORIGUAL;
					break;
				case MAIORIGUAL:
					notOp = OpType.MENOR;
					break;
				default:
					break;
				}
				Node.icg.gen(Quadruple.branch(notOp, this.falseL, exp1.addr, exp2.addr));
			} else {
				Node.icg.gen(Quadruple.branch(op, this.trueL, exp1.addr, exp2.addr));
				Node.icg.gen(Quadruple.jump(this.falseL));
			}
		} else if (op == OpType.AND){
			exp1.trueL = EQLabel.FALL;
			exp1.falseL = (this.falseL.equals(EQLabel.FALL))? new EQLabel(): this.falseL;
			exp2.trueL = this.trueL;
			exp2.falseL = this.falseL;
			exp1.genCode();
			exp2.genCode();
			if (this.falseL.equals(EQLabel.FALL)) {
				EQLabel.useLabel(exp1.falseL);
			}
		} else {
			exp1.genCode();
			exp2.genCode();
			this.addr = new EQTemp();
			Node.icg.gen(Quadruple.binary(op, this.addr, exp1.addr, exp2.addr));
		}
	}
}
